#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "camerastrategy.h"
#include "videostrategy.h"
#include <iostream>
#include <qtimer.h>
#include <opencv2/core/core.hpp>
#include <math.h>
#include <QDebug>
#include <QHBoxLayout>
#include <QDateTime>
#include <QComboBox>
#include <QProgressBar>

using namespace cv;
using namespace std;

cv::Mat sign(const cv::Mat& input)
{
    cv::Mat positive = (input > 0) / 255;  // 正数区域为1
    cv::Mat negative = (input < 0) / 255;  // 负数区域为1
    return positive - negative;            // 合并结果：1（正）、0（零）、-1（负）
}

std::vector<int> MainWindow::listAvailableCameras(int maxTested)
{
    std::vector<int> availableCameras;
    for (int i = 0; i < maxTested; i++) {
        cv::VideoCapture cap(i);
        if (cap.isOpened()) {
            availableCameras.push_back(i);
            cap.release();
        }
    }
    return availableCameras;
}

void MainWindow::Init()
{
    QFont font = QFont("Microsoft YaHei" , 10 ,  QFont::DemiBold);
    QFont font1 = QFont("Microsoft YaHei" , 10 ,  QFont::DemiBold);

    QString grpBoxStyle = "QGroupBox {border-width: 1px; border-style: solid;border-color: rgb(0, 0, 0); color: black; }";
    QGroupBox *qBox = new QGroupBox("Result Display",this);
    qBox->setGeometry(30, 30, 350, 120);
    qBox->setFont(font);
    qBox->setStyleSheet(grpBoxStyle);

    for (int i = 0; i < 5; i++) {
        rslt[i] = new QLabel(qBox);
        rslt[i]->setGeometry(35+60*i, 50, 40, 40);
        rslt[i]->setText(QString::number(i));
        rslt[i]->setStyleSheet("QLabel { border-width: 1px; border-style: solid;border-color: rgb(0, 0, 0); background: yellow ;color: black; font: 30px Calibri;}");
        rslt[i]->setAlignment(Qt::AlignCenter);
    }

    for (int i = 0; i < 3; i++) {
        fpnImgLabel[i] = new QLabel(this);
        fpnImgLabel[i]->setGeometry(450, 30, 1280, 720);
    }

    QGroupBox *paramGrp = new QGroupBox("Parameter Adjust", this);
    paramGrp->setGeometry(30, 200, 350, 200);
    paramGrp->setFont(font);
    paramGrp->setStyleSheet(grpBoxStyle);

    itsRestltLable = new QLabel(qBox);
    itsRestltLable->setGeometry(35+60*2, 10, 40, 40);
    itsRestltLable->setText("result");
    itsRestltLable->setAlignment(Qt::AlignCenter);

    QLabel *param[4];
    for(int i = 0; i < 4; i++){
        // level
        param[i] = new QLabel(paramGrp);
        param[i]->setGeometry(45, 40 + 35*i, 90, 40);
        param[i]->setText("Level" + QString::number(i+1));
        param[i]->setFont(font1);

        // editable text
        level[i] = new QLineEdit(paramGrp);
        level[i]->setGeometry(160, 50 + 35*i, 120, 30);
        level[i]->setFont(font1);
    }
    level[0]->setText(QString::number(20));
    level[1]->setText(QString::number(40));
    level[2]->setText(QString::number(60));
    level[3]->setText(QString::number(80));

    QGroupBox *btnBox = new QGroupBox("Acquisition Control", this);
    btnBox->setGeometry(30, 450, 350, 300);
    btnBox->setFont(font);
    btnBox->setStyleSheet(grpBoxStyle);

    QString btnStyle = "QPushButton{font: Microsoft YaHei, 10, 75;;color:black;} \
        QPushButton{background-color:#cccccc; border-width: 1px; border-style: solid;border-color: black;}\
        QPushButton:hover{background-color:rgb(150, 170, 200)}";


    QPushButton *pBtnStartCamera = new QPushButton("Capture Grabber",btnBox);
    pBtnStartCamera->setObjectName("StartCamera");
    pBtnStartCamera->setGeometry(40, 110, 100, 40);
    pBtnStartCamera->setStyleSheet(btnStyle);

    QPushButton *pBtnStartVideo = new QPushButton("Video Grabber",btnBox);
    pBtnStartVideo->setObjectName("StartVideo");
    pBtnStartVideo->setGeometry(150, 110, 100, 40);
    pBtnStartVideo->setStyleSheet(btnStyle);

    QPushButton *pBtnStop = new QPushButton("Stop",btnBox);
    pBtnStop->setObjectName("Stop");
    pBtnStop->setGeometry(40, 170, 100, 40);
    pBtnStop->setStyleSheet(btnStyle);

    QPushButton *pBtnReset = new QPushButton("Reset",btnBox);
    pBtnReset->setObjectName("Reset");
    pBtnReset->setGeometry(150, 170, 100, 40);
    pBtnReset->setStyleSheet(btnStyle);

    yAxis = new QLineEdit(btnBox);
    yAxis->setGeometry(50, 230, 90, 40);
    yAxis->setFont(font1);

    QPushButton *pBtnY = new QPushButton("Set Axis Y",btnBox);
    pBtnY->setObjectName("SetY");
    pBtnY->setGeometry(150, 230, 150, 40);
    pBtnY->setStyleSheet(btnStyle);

    QButtonGroup *btnGrp = new QButtonGroup;
    btnGrp->addButton(pBtnStartCamera);
    btnGrp->addButton(pBtnStop);
    btnGrp->addButton(pBtnReset);
    btnGrp->addButton(pBtnY);
    btnGrp->addButton(pBtnStartVideo);

    connect(btnGrp, SIGNAL(buttonReleased(QAbstractButton*)), this, SLOT(OnButtonClicked(QAbstractButton*)));

    qDebug() << "Available ports:";

    seriaComboBox= new QComboBox(btnBox);
    seriaComboBox->setObjectName("SeriaComboBox");
    seriaComboBox->setGeometry(40, 30, 200, 30);
    seriaComboBox->setStyleSheet(btnStyle);

    cameraComboBox= new QComboBox(btnBox);
    cameraComboBox->setObjectName("CameraComboBox");
    cameraComboBox->setGeometry(40, 70, 100, 30);
    cameraComboBox->setStyleSheet(btnStyle);

    for (const auto &port : QSerialPortInfo::availablePorts()) {
        qDebug() << port.portName();
        QString portInfo = port.portName();
        if (!port.description().isEmpty()) {
            portInfo += " - " + port.description();
        }
        seriaComboBox->addItem(portInfo, port.portName());
    }

    connect(seriaComboBox, QOverload<int>::of(&QComboBox::activated),
            this, &MainWindow::onComboBoxDataChanged);
    itsSerialPtr = nullptr;
    itsLedBrightness = 200;

    itsBriLable = new QLabel(btnBox);
    itsBriLable->setGeometry(250, 30, 60, 30);
    itsBriLable->setText("当前亮度");

    itsLogLable = new QLabel(btnBox);
    itsLogLable->setGeometry(300, 30, 60, 30);
    itsLogLable->setText("LOG");

    itsDarkLable = new QLabel(btnBox);
    itsDarkLable->setGeometry(250, 55, 60, 30);
    itsDarkLable->setText("过暗比例");

    itsExposureLable = new QLabel(btnBox);
    itsExposureLable->setGeometry(250, 70, 60, 30);
    itsExposureLable->setText("过曝比例");

    std::vector<int> cams = listAvailableCameras();
    if (cams.empty())
    {
        qDebug() << "未检测到可用摄像头！";
    }
    else
    {
        for (int cam : cams)
        {
            cameraId = cam;
            cameraComboBox->addItem(std::to_string(cam).c_str(), cam);
        }
    }
    connect(cameraComboBox, QOverload<int>::of(&QComboBox::activated),
            this, &MainWindow::onCameraIndexChanged);

    // 创建进度条
    progressBar = new CustomProgressBar(this); // this 指定父对象
    progressBar->setObjectName("VideoProgressBar");     // 设置对象名称
    progressBar->setGeometry(30, 760, 350, 20);          // 设置位置和大小 (x, y, width, height)

    // 设置样式表（参考你的 btnStyle）
    QString progressStyle = R"(
        QProgressBar {
            border: 2px solid #cccccc;
            border-radius: 5px;
            text-align: center;
            background-color: #f0f0f0;
        }
        QProgressBar::chunk {
            background-color: #4CAF50;
            border-radius: 3px;
        }
    )";
    progressBar->setStyleSheet(progressStyle);

    // 设置进度条范围和其他属性
    progressBar->setRange(0, 100);    // 设置范围 0-100
    progressBar->setValue(0);        // 设置当前值
    progressBar->setTextVisible(true); // 显示百分比文本
    connect(progressBar, &CustomProgressBar::progressClicked,
            this, &MainWindow::onProcessBarClicked);
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setFixedSize(1300,800);
    setWindowTitle("Demo");
    m_timer = new QTimer(this);
    m_timer->setSingleShot(false);
    connect(m_timer,SIGNAL(timeout()),this,SLOT(OnUpdate()));
    m_timer->start(26);

    // initialize the window
    Init();

    // curve
    maxSize = 31; // 只存储最新的 31 个数据
    maxX = 31;
    maxY = 127;

    splineSeries = new QSplineSeries(this);
    scatterSeries = new QScatterSeries(this);
    scatterSeries->setMarkerSize(8);

    chart = new QChart();
    chart->addSeries(splineSeries);
    chart->addSeries(scatterSeries);
    chart->legend()->hide();
    chart->setTitle("Realtime display");
    chart->createDefaultAxes();
    chart->axisX()->setRange(0, maxX);
    chart->axisY()->setRange(0, maxY);

    chartView = new QChartView(chart, this);
    chartView->setRenderHint(QPainter::Antialiasing);
    chartView->setGeometry(400,20,900,750);

    QHBoxLayout *layout = new QHBoxLayout();
    layout->setContentsMargins(400, 0, 0, 0);
    layout->addWidget(chartView);
    setLayout(layout);

    timerId = startTimer(26);
    // qsrand(QDateTime::currentDateTimeUtc().toTime_t());
}



void MainWindow::onComboBoxDataChanged(int index)
{
    QVariant data = seriaComboBox->itemData(index);
    qDebug() << "存储的数据:" << data.toString();

    // 根据数据类型处理
    if (data.canConvert<int>()) {
        int value = data.toInt();
        qDebug() << "转换为整数:" << value;
    }

    if(itsSerialPtr)
    {
        itsSerialPtr->close();
        itsSerialPtr.reset();
    }

    itsSerialPtr = std::make_unique<QSerialPort>();
    itsSerialPtr->setPortName(data.toString());
    itsSerialPtr->setBaudRate(QSerialPort::Baud9600);
    if (!itsSerialPtr->open(QIODevice::ReadWrite)) {
        qDebug() << "无法打开串口:" << itsSerialPtr->errorString();
        return;
    }
    if(!itsSerialRecvThread)
    {
        itsSerialRecvThread = std::make_unique<std::thread>([this](){
            isThreadRunning = true;
            serialRead();
        });
    }
}

void MainWindow::onCameraIndexChanged(int index)
{
    QVariant data = cameraComboBox->itemData(index);
    cameraId = std::stoi(data.toString().toStdWString());
    qDebug() << "using camera id:" << cameraId;
}

void MainWindow::onProcessBarClicked(int value)
{
    if(itsVideoStragey)
    {
        itsVideoStragey->setFrameIndex(value);
    }
    else
    {
        QMessageBox mesg;
        mesg.warning(this,"Tip","请先选择视频文件，或者打开摄像头！");
    }
}

int MainWindow::serialSend(const uint8_t& data)
{
    QByteArray dataArray(4, 0);
    dataArray[0] = 0x31;
    dataArray[1] = 0x31;
    dataArray[2] = data;
    dataArray[3] = 0xff;
    int ret = -1;

    if(itsSerialPtr)
    {
        itsSerialPtr->write(dataArray);
        itsSerialPtr->waitForBytesWritten(1000);
    }
    return ret;
}

void MainWindow::serialRead()
{
    while(isThreadRunning)
    {
        if(itsSerialPtr && itsSerialPtr->isOpen())
        {
            if (itsSerialPtr->waitForReadyRead(100))// 等待100ms
            {
                QByteArray data = itsSerialPtr->readAll();
                while (itsSerialPtr->waitForReadyRead(10))
                {
                    data += itsSerialPtr->readAll();
                }
                // 如果是十六进制数据显示
                // qDebug() << "十六进制:" << data.toHex();
                if(data[0] == 0x32 && data[1] == 0x32 && (uint8_t)data[3] == 0xff) // {0x32, 0x32, data, 0xff}
                {
                    // itsLedBrightness = (uint8_t)data[2];
                    // qDebug() << "更新亮度:" << (int)itsLedBrightness;
                }
            }
        }
        else
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }

}

void MainWindow::serialClose()
{
    if(itsSerialRecvThread && itsSerialRecvThread->joinable())
    {
        isThreadRunning = false;
        itsSerialRecvThread->join();
    }
    if(itsSerialPtr && itsSerialPtr->isOpen())
    {
        itsSerialPtr->close();
    }
}

cv::Mat QImage2cvMat(QImage image)
{
    cv::Mat mat;
    //    qDebug() << image.format();
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
        break;
    case QImage::Format_RGB888:
        mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
        cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
        mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
        break;
    }
    return mat;
}

void MainWindow::OnButtonClicked(QAbstractButton *button)
{
    qDebug() << "Name:" << button->objectName().toStdString();

    if(button->objectName() == "StartVideo")
    {
        if(!itsVideoStragey)
        {
            itsVideoStragey = std::make_unique<VideoStrategy>();
        }
        state = itsVideoStragey->prepareVideoCapture() == 0 ? START : STOP;
        progressBar->setPercentage(0);
    }
    else if (button->objectName() == "StartCamera")
    {
        // 重新抓一张基准图
        if(!itsVideoStragey)
        {
            // 直接读取摄像头分辨率是640*480的 需要手动设置宽高
            itsVideoStragey = std::make_unique<CameraStrategy>(WIDTH, HEIGHT, cameraId);
        }
        state = itsVideoStragey->prepareVideoCapture() == 0 ? START : STOP;
    }
    else if (button->objectName() == "Stop")
    {
        // sensor stop
        if(itsVideoStragey)
        {
            state = state == START ? STOP : START;
        }
        else
        {
            state = STOP;
        }
        // DataStorer::instance().close();
    }
    else if (button->objectName() == "Reset")
    {
        // sensor reset
        state = STOP;
        itsVideoStragey = nullptr;
        DataStorer::instance().close();
    }
    else if ( button->objectName() == "SetY")
    {
        maxY = yAxis->text().toInt();
        chart->axisY()->setRange(0, maxY);
    }

    int count = 0;//过曝过暗场景调节30帧，不行就不调了
    while(state != STOP)
    {
        // 1. 抓原图
        if(itsVideoStragey->getFrame(frame2) != 0)
        {
            QMessageBox mesg;
            mesg.warning(this,"Tip","无效帧，结束分析!");
            DataStorer::instance().close();
            state = STOP;
            progressBar->setPercentage(0);
            itsVideoStragey->setFrameIndex(0);
            break;
        }
        cv::imshow("raw", frame2);
        // 2. 设置为灰图
        cv::cvtColor(frame2, frame2, cv::COLOR_RGB2GRAY);
        // 3. 过曝检查
        const float threshold = 20.0f;
        auto res = exposureAnalysisEx(frame2, threshold);
        if(res.Exposure > threshold || res.Dark > threshold)
        {
            // 一直调
            // state = OVEREXPOSURE;

            // 调不过来了就不调了
            if(itsLedBrightness >= 200 || itsLedBrightness <= 0)
            {
                count++;
                if(count > 3)
                {
                    state = START;
                }
                else
                {
                    state = OVEREXPOSURE;
                }
            }
            else
            {
                state = OVEREXPOSURE;
            }
        }
        else
        {
            count = 0;
            state = START;
        }


        itsBriLable->setText(std::to_string(itsLedBrightness).c_str());
        itsLogLable->setText(std::to_string(count).c_str());

        switch (state)
        {
        case OVEREXPOSURE:
        {
            OverexposureCompensation(frame2, res, threshold);
            break;
        }
        case START:
        {
            currValidPixel = itsVideoStragey->AnalyzeTheRainfallTrend(frame2);
            itsRestltLable->setText(std::to_string(currValidPixel).c_str());
            float progress = (float)itsVideoStragey->getFrameIndex()/(float)itsVideoStragey->getFrameTotal()*100;
            progressBar->setPercentage(progress);
            DataStorer::instance().write(progress, currValidPixel);
            break;
        }
        default:
            break;
        }

        while (state == STOP)
        {
            if(state == START)
            {
                break;
            }
            waitKey(26);
        }
        esc = waitKey(26);
        if(esc == 27)
        {
            cout << "exit" <<endl;
            state = STOP;
        }
    }

}


void MainWindow::OverexposureCompensation(cv::Mat& gray, const BrightnessAnalysis& analysis, float threshold)
{
    int bri = (int)itsLedBrightness;
    if(analysis.Exposure > threshold)
    {
        bri -= 10;
        if(bri < 0)
        {
            bri = 0;
        }
    }
    else if(analysis.Dark > threshold)
    {
        bri += 10;
        if(bri > 200)
        {
            bri = 200;
        }
    }

    serialSend((uint8_t)bri);
    itsLedBrightness = bri;
    // qDebug() << "-------" << itsLedBrightness;
}

void MainWindow::OnUpdate() {
    QString defaultsty = "QLabel { border-width: 1px; border-style: solid;border-color: rgb(0, 0, 0); background: yellow ;color: black; font: 20px Calibri;}";
    QString style1 = "QLabel { border-width: 1px; border-style: solid;border-color: rgb(0, 0, 0); background: red ;color: black; font: 20px Calibri;}";
    //    cout << "update level "<< endl;
    if(state == START) {
        //        std::cout << cnt << std::endl;
        //        currValidPixel+=50;
        if (currValidPixel < level[0]->text().toInt()) {
            rslt[0]->setStyleSheet(style1);
            rslt[1]->setStyleSheet(defaultsty);
            rslt[2]->setStyleSheet(defaultsty);
            rslt[3]->setStyleSheet(defaultsty);
            rslt[4]->setStyleSheet(defaultsty);
        } else if ( currValidPixel <level[1]->text().toInt()) {
            rslt[1]->setStyleSheet(style1);
            rslt[0]->setStyleSheet(defaultsty);
            rslt[2]->setStyleSheet(defaultsty);
            rslt[3]->setStyleSheet(defaultsty);
            rslt[4]->setStyleSheet(defaultsty);
        } else if (currValidPixel < level[2]->text().toInt()) {
            rslt[2]->setStyleSheet(style1);
            rslt[0]->setStyleSheet(defaultsty);
            rslt[1]->setStyleSheet(defaultsty);
            rslt[3]->setStyleSheet(defaultsty);
            rslt[4]->setStyleSheet(defaultsty);
        } else if (currValidPixel < level[3]->text().toInt()) {
            rslt[3]->setStyleSheet(style1);
            rslt[0]->setStyleSheet(defaultsty);
            rslt[1]->setStyleSheet(defaultsty);
            rslt[2]->setStyleSheet(defaultsty);
            rslt[4]->setStyleSheet(defaultsty);
        } else {
            rslt[4]->setStyleSheet(style1);
            rslt[0]->setStyleSheet(defaultsty);
            rslt[1]->setStyleSheet(defaultsty);
            rslt[2]->setStyleSheet(defaultsty);
            rslt[3]->setStyleSheet(defaultsty);
        }
    }
}

void MainWindow::dataReceived(int value) {
    data << value;
    // 数据个数超过了最大数量，则删除最先接收到的数据，实现曲线向前移动
    while (data.size() > maxSize) {
        data.removeFirst();
    }

    // 界面被隐藏后就没有必要绘制数据的曲线了
    if (isVisible()) {
        splineSeries->clear();
        scatterSeries->clear();
        int dx = maxX / (maxSize-1);
        int less = maxSize - data.size();

        for (int i = 0; i < data.size(); ++i) {
            splineSeries->append(less*dx+i*dx, data.at(i));
            scatterSeries->append(less*dx+i*dx, data.at(i));
        }
    }
}

void MainWindow::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == timerId && state == START) {
        int newData = currValidPixel;
        // cout << "newData =" <<newData <<endl;
        dataReceived(newData);
    }
}


BrightnessAnalysis MainWindow::exposureAnalysis(cv::Mat& image, float threshold ,double overExposureThreshold, double underExposureThreshold)
{
    BrightnessAnalysis result;
    cv::Mat gray;
    cv::cvtColor(frame2, gray, cv::COLOR_BGR2GRAY);

    result = exposureAnalysisEx(gray);

    return result;
}


BrightnessAnalysis MainWindow::exposureAnalysisEx(cv::Mat& gray, float threshold, double overExposureThreshold, double underExposureThreshold)
{
    BrightnessAnalysis result;
    // 统计总像素数
    int totalPixels = gray.rows * gray.cols;

    // 计算过曝像素(亮度>threshold)
    cv::Mat overExposed = gray > overExposureThreshold;
    int overExposedPixels = cv::countNonZero(overExposed);
    result.Exposure = (static_cast<double>(overExposedPixels) / totalPixels) * 100;

    // 计算过暗像素(亮度<threshold)
    cv::Mat underExposed = gray < underExposureThreshold;
    int underExposedPixels = cv::countNonZero(underExposed);
    result.Dark = (static_cast<double>(underExposedPixels) / totalPixels) * 100;

    std::stringstream sstr1;
    sstr1 << "过暗:";
    sstr1 << result.Dark;
    itsDarkLable->setText(sstr1.str().c_str());

    std::stringstream sstr2;
    sstr2 << "过曝:";
    sstr2 << result.Exposure;
    itsExposureLable->setText(sstr2.str().c_str());

    if(result.Exposure > threshold)
    {
        // 显示过曝区域(可选)
        cv::Mat display;
        cv::cvtColor(gray, display, cv::COLOR_GRAY2BGR);
        display.setTo(cv::Scalar(0, 0, 255), overExposed); // 红色标记过曝区域
        cv::imshow("Overexposed Areas", display);
    }
    if(result.Dark > threshold)
    {
        // 显示过曝区域(可选)
        cv::Mat display;
        cv::cvtColor(gray, display, cv::COLOR_GRAY2BGR);
        display.setTo(cv::Scalar(255, 0, 0), overExposed); // 黑色色标记过曝区域
        cv::imshow("Dark Areas", display);
    }

    return result;
}

MainWindow::~MainWindow()
{
    delete ui;
    serialClose();
}

// 两张图每一步操作是什么样的
